/**
  ******************************************************************************
  * @author     Chris
  * @since      2024/6/18 22:21
  *
  * @file       pd_adc_reg.c
  * @brief      ADC Regular group peripheral driver.
  *
  * @note       This file contains the peripheral driver for the ADC Regular group.
  *
  * @warning    None.
  ******************************************************************************
  * Change Logs:
  *   Date          Author       Notes
  *   2024/6/18     Chris        the first version
  *
  ******************************************************************************
  */


#include <stm32f4xx_ll_adc.h>
#include "pd_adc_reg.h"

#define ADC_MIN(m, n) m>=n?n:m

static void ADCRegular_init(ADCRegular *this) {
    // Set ADC clock, GPIO, ADC and ADC common register
    this->adcBase->init(this->adcBase);

    // ADC Regular Group
    LL_ADC_REG_InitTypeDef ADC_REG_InitStruct = {0};
    LL_ADC_REG_StructInit(&ADC_REG_InitStruct);
    ADC_REG_InitStruct.TriggerSource    = this->TriggerSource;         // ADC REG trigger source: timer, External Pin, Software
    ADC_REG_InitStruct.SequencerLength  = this->SequencerLength;     // ADC_SQR1_L, Bits 23:20 L[3:0]: Regular channel sequence length
    ADC_REG_InitStruct.SequencerDiscont = this->SequencerDiscont;   // Bit 11 DISCEN: Discontinuous mode on regular channels; Bits 15:13 DISCNUM[2:0]: Discontinuous mode channel count
    ADC_REG_InitStruct.ContinuousMode   = this->ContinuousMode;       // ADC_CR2_CONT, Bit 1 CONT: Continuous conversion
    ADC_REG_InitStruct.DMATransfer      = this->DMATransfer;             // ADC DMA transfer
    LL_ADC_REG_Init(this->adcBase->ADCx, &ADC_REG_InitStruct);

    // ADC_CR2_EOCS bit10 : End of conversion selection. 0, EOC=1 if sequence end; 1, EOC=1 when every regular conversion.
    LL_ADC_REG_SetFlagEndOfConversion(this->adcBase->ADCx, LL_ADC_REG_FLAG_EOC_UNITARY_CONV);

    if (this->adcBase->adcChannel->channels) {
        uint8_t size = ADC_MIN(this->adcBase->adcChannel->chSize, this->adcBase->adcChannel->rankSize);
        for (int i = 0; i < size; ++i) {
            LL_ADC_REG_SetSequencerRanks(this->adcBase->ADCx, this->adcBase->adcChannel->ranks[i],
                                         this->adcBase->adcChannel->channels[i]);
            LL_ADC_SetChannelSamplingTime(this->adcBase->ADCx, this->adcBase->adcChannel->channels[i],
                                          LL_ADC_SAMPLINGTIME_56CYCLES);
        }
    }

    LL_ADC_Enable(this->adcBase->ADCx);

    uint32_t wait_loop_index = LL_ADC_DELAY_TEMPSENSOR_STAB_US * (SystemCoreClock / 100000 / 2) / 10;
    while (wait_loop_index--);
}

static void ADCRegular_start(ADCRegular *this) {
    LL_ADC_REG_StartConversionSWStart(this->adcBase->ADCx);
}

static uint16_t ADCRegular_getValue(ADCRegular *this, uint32_t Rank, uint32_t Channel, uint32_t SamplingTime) {
    LL_ADC_REG_SetSequencerRanks(this->adcBase->ADCx, Rank, Channel);
    LL_ADC_SetChannelSamplingTime(this->adcBase->ADCx, Channel, SamplingTime);

    // Start conversion
    LL_ADC_REG_StartConversionSWStart(this->adcBase->ADCx);
    while (LL_ADC_IsActiveFlag_EOCS(this->adcBase->ADCx) == RESET);
    LL_ADC_ClearFlag_EOCS(this->adcBase->ADCx);
    return LL_ADC_REG_ReadConversionData12(this->adcBase->ADCx);
}


static ADCRegular *build(ADCBase *adcBase, uint32_t TriggerSource, uint32_t SequencerLength, uint32_t ContinuousMode,
                         uint32_t DMATransfer) {
    ADCRegular *adcRegular = malloc(sizeof(ADCRegular));

    Driver_add(adcRegular, GEN_PERM);

    adcRegular->adcBase          = adcBase;
    adcRegular->TriggerSource    = TriggerSource;
    adcRegular->SequencerLength  = SequencerLength;
    adcRegular->SequencerDiscont = LL_ADC_REG_SEQ_DISCONT_DISABLE;
    adcRegular->ContinuousMode   = ContinuousMode;
    adcRegular->DMATransfer      = DMATransfer;

    adcRegular->init     = &ADCRegular_init;
    adcRegular->start    = &ADCRegular_start;
    adcRegular->getValue = &ADCRegular_getValue;

    return adcRegular;
}

const struct ADCRegularClass ADCRegularBuilder = {.build = &build};
